﻿using System;
using System.Linq;
using System.ServiceProcess;
using System.Threading;
using System.Messaging;
using System.IO.Ports;
using System.Configuration;

namespace GPSTrackingNotifierServerService
{
    public partial class GPSTrackingNotifierServerService : ServiceBase
    {
        //Message Queue
        [Serializable]
        public  class GPSMessageQueue
        {
            public string Imei;
            public GPSTrackingDBV2.Entities.TrackDataLastLocation TrackDataLastLocationEntity;
        }

        public static string _MessageQueueName = @Properties.Settings.Default.MessageQueueName;

        public static ManualResetEvent allDone = new ManualResetEvent(false);
        public static Thread TCPListenerThread = new Thread(StartReceive);

        private static bool IsThreadAlive = true;

        public GPSTrackingNotifierServerService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                GPSTrackingDBCustom.Log.MessageQueueLog.Info("--- GPS Tracking Server Start Notifier V3 ---", "Notifier");
                GPSTrackingDBCustom.Log.MessageQueueLog.Info("IP: " + System.Net.Dns.GetHostName(), "Notifier");

                IsThreadAlive = true;
                TCPListenerThread.Start();
            }
            catch (Exception ex)
            {
                GPSTrackingDBCustom.Log.MessageQueueLog.Error("Error..... Message:" + ex.Message, "Notifier");
                GPSTrackingDBCustom.Log.MessageQueueLog.Error("Notifier Server is DOWN", "Notifier");
                this.Stop();
            }
        }

        protected override void OnStop()
        {
            IsThreadAlive = false;
            GPSTrackingDBCustom.Log.MessageQueueLog.Warning("GPS Tracking Nofifier Server Is Down","Notifier");
        }

        public static void StartReceive()
        {
            try
            {
                MessageQueue MessageQueue = new MessageQueue(_MessageQueueName);

                while (IsThreadAlive)
                {
                    // Set the event to nonsignaled state.
                    allDone.Reset();

                    // Start an asynchronous socket to listen for connections.
                    GPSTrackingDBCustom.Log.MessageQueueLog.Info("Waiting for a message .....", "Notifier");

                    // Create an instance of MessageQueue. Set its formatter.                    
                    MessageQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(GPSMessageQueue) });

                    // Add an event handler for the ReceiveCompleted event.
                    MessageQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(ReceiveCompleted);

                    // Begin the asynchronous receive operation.
                    MessageQueue.BeginReceive();

                    // Wait until a connection is made before continuing.
                    allDone.WaitOne();
                }
            }
            catch (System.Messaging.MessageQueueException ex)
            {
                GPSTrackingDBCustom.Log.MessageQueueLog.Error("Error Waiting for a connection..... Message:" + ex.Message, "Notifier");
                TCPListenerThread.Abort();
            }
        }

        private static void ReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
        {
            // Connect to the queue.
            MessageQueue MessageQueue = (MessageQueue)source;

            // End the asynchronous Receive operation.
            Message MessageXML = MessageQueue.EndReceive(asyncResult.AsyncResult);

            
            //Process
            GPSMessageQueue GPSMessageQueue = (GPSMessageQueue)MessageXML.Body;
            string Message = string.Empty;

            GPSTrackingDBCustom.Log.MessageQueueLog.Info("Process ... Imei:"+GPSMessageQueue.Imei, "Notifier");
            //Has Geofence
            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.NotificationByGeoFencesByGpsStock> NotificationByGeoFencesByGpsStockEntities = GPSTrackingDBV2.Data.DataRepository.NotificationByGeoFencesByGpsStockProvider.GetByGpsImei(GPSMessageQueue.Imei);

            if (NotificationByGeoFencesByGpsStockEntities.Count > 0)
            {
                foreach (GPSTrackingDBV2.Entities.NotificationByGeoFencesByGpsStock entity in NotificationByGeoFencesByGpsStockEntities)
                {
                    //Notification GeoFence OUT
                    if (entity.NotificationId == 1)
                    {
                        //Has Geofence Notification Today?
                        GPSTrackingDBV2.Entities.TrackDataLastLocationNotifications TrackDataLastLocationNotificationEntity = GPSTrackingDBV2.Data.DataRepository.TrackDataLastLocationNotificationsProvider.GetByGpsImeiNotificationIdGeoFenceId(GPSMessageQueue.Imei, entity.NotificationId, entity.GeoFenceId);

                        if (TrackDataLastLocationNotificationEntity == null)
                        {
                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.GeoFenceDetails> GeoFenceDetailEntities = GPSTrackingDBV2.Data.DataRepository.GeoFenceDetailsProvider.GetByGeoFenceId(entity.GeoFenceId);
                            if (!IsPointInPolygon(GeoFenceDetailEntities, GPSMessageQueue.TrackDataLastLocationEntity))
                            {
                                Message = Properties.Settings.Default.MessageGeoFenceOUT;

                                //Insert Notification
                                InsertNotification(ref Message, GPSMessageQueue.Imei, entity.GeoFenceId, entity.NotificationId);

                                //Send email to Admins
                                if (entity.SendToAdmin)
                                {

                                    System.Data.DataTable dtAdmin = GPSTrackingDBV2Custom.Data.DataRepository.GPSStockProvider.GetAdminUsernameByGPSImei(GPSMessageQueue.Imei);

                                    string AdminUsername;
                                    int CompanyID;
                                    System.Data.DataRow row;

                                    if (dtAdmin.Rows.Count > 0)
                                    {

                                        for (int i = 0; i < dtAdmin.Rows.Count; i++)
                                        {

                                            row = dtAdmin.Rows[i];

                                            AdminUsername = row["AdminUsername"].ToString();
                                            CompanyID = int.Parse(row["CompanyID"].ToString());

                                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.AdministratorsByEmail> AdministratorsByEmailEntities = GPSTrackingDBV2.Data.DataRepository.AdministratorsByEmailProvider.GetByAdminUsernameCompanyId(AdminUsername, CompanyID);

                                            foreach (GPSTrackingDBV2.Entities.AdministratorsByEmail entityMail in AdministratorsByEmailEntities)
                                            {
                                                GPSTrackingEmailManager.Manager.Send(entityMail.Email, entityMail.Email, Message, true);
                                                GPSTrackingDBCustom.Log.MessageQueueLog.Info("Admin Notification Sending to: " + entityMail.Email, "Notifier");
                                            }
                                        }
                                    }
                                }


                                //Send email to Users
                                if (entity.SendToUser)
                                {
                                    System.Data.DataTable tdUser = GPSTrackingDBV2Custom.Data.DataRepository.GPSStockProvider.GetUsernameByGPSImei(GPSMessageQueue.Imei);

                                    string Username;
                                    int CompanyID;
                                    System.Data.DataRow row;

                                    if (tdUser.Rows.Count > 0)
                                    {

                                        for (int i = 0; i < tdUser.Rows.Count; i++)
                                        {

                                            row = tdUser.Rows[i];

                                            Username = row["Username"].ToString();
                                            CompanyID = int.Parse(row["CompanyID"].ToString());

                                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.UsersByEmail> UsersByEmailEntities = GPSTrackingDBV2.Data.DataRepository.UsersByEmailProvider.GetByUsernameCompanyId(Username, CompanyID);

                                            foreach (GPSTrackingDBV2.Entities.UsersByEmail entityMail in UsersByEmailEntities)
                                            {
                                                GPSTrackingEmailManager.Manager.Send(entityMail.Email, entityMail.Email, Message, true);
                                                GPSTrackingDBCustom.Log.MessageQueueLog.Info("User Notification Sending to: " + entityMail.Email, "Notifier");
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.GeoFenceDetails> GeoFenceDetailEntities = GPSTrackingDBV2.Data.DataRepository.GeoFenceDetailsProvider.GetByGeoFenceId(entity.GeoFenceId);
                            if (IsPointInPolygon(GeoFenceDetailEntities, GPSMessageQueue.TrackDataLastLocationEntity))
                            {
                                GPSTrackingDBV2.Data.DataRepository.TrackDataLastLocationNotificationsProvider.Delete(TrackDataLastLocationNotificationEntity);
                            }
                        }
                    }

                    //Notification GeoFence IN
                    if (entity.NotificationId == 2)
                    {
                        //Has Geofence Notification Today?
                        GPSTrackingDBV2.Entities.TrackDataLastLocationNotifications TrackDataLastLocationNotificationEntity = GPSTrackingDBV2.Data.DataRepository.TrackDataLastLocationNotificationsProvider.GetByGpsImeiNotificationIdGeoFenceId(GPSMessageQueue.Imei, entity.NotificationId, entity.GeoFenceId);

                        if (TrackDataLastLocationNotificationEntity == null)
                        {
                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.GeoFenceDetails> GeoFenceDetailEntities = GPSTrackingDBV2.Data.DataRepository.GeoFenceDetailsProvider.GetByGeoFenceId(entity.GeoFenceId);
                            if (IsPointInPolygon(GeoFenceDetailEntities, GPSMessageQueue.TrackDataLastLocationEntity))
                            {

                                Message = Properties.Settings.Default.MessageGeoFenceIN;

                                //Insert Notification
                                InsertNotification(ref Message, GPSMessageQueue.Imei, entity.GeoFenceId, entity.NotificationId);

                                //Send email to Admins
                                if (entity.SendToAdmin)
                                {
                                    System.Data.DataTable dtAdmin = GPSTrackingDBV2Custom.Data.DataRepository.GPSStockProvider.GetAdminUsernameByGPSImei(GPSMessageQueue.Imei);
                                    
                                    string AdminUsername;
                                    int CompanyID;
                                    System.Data.DataRow row;

                                    if (dtAdmin.Rows.Count > 0)
                                    {

                                        for (int i = 0; i < dtAdmin.Rows.Count; i++)
                                        {

                                            row = dtAdmin.Rows[i];

                                            AdminUsername = row["AdminUsername"].ToString();
                                            CompanyID = int.Parse(row["CompanyID"].ToString());

                                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.AdministratorsByEmail> AdministratorsByEmailEntities = GPSTrackingDBV2.Data.DataRepository.AdministratorsByEmailProvider.GetByAdminUsernameCompanyId(AdminUsername, CompanyID);
                                            foreach (GPSTrackingDBV2.Entities.AdministratorsByEmail entityMail in AdministratorsByEmailEntities)
                                            {
                                                try
                                                {
                                                    GPSTrackingEmailManager.Manager.Send(entityMail.Email, entityMail.Email, Message, true);
                                                    GPSTrackingDBCustom.Log.MessageQueueLog.Info("Admin Notification Sending to: " + entityMail.Email, "Notifier");
                                                }
                                                catch (Exception ex)
                                                {
                                                    GPSTrackingDBCustom.Log.MessageQueueLog.Error("Error Send Email:" + ex.Message, "Notifier");
                                                }

                                            }
                                        }
                                    }
                                }


                                //Send email to Users
                                if (entity.SendToUser)
                                {
                                    System.Data.DataTable dtUser = GPSTrackingDBV2Custom.Data.DataRepository.GPSStockProvider.GetUsernameByGPSImei(GPSMessageQueue.Imei);
                                    
                                    string Username;
                                    int CompanyID;
                                    System.Data.DataRow row;

                                    if (dtUser.Rows.Count > 0)
                                    {

                                        for (int i = 0; i < dtUser.Rows.Count; i++)
                                        {

                                            row = dtUser.Rows[i];

                                            Username = row["Username"].ToString();
                                            CompanyID = int.Parse(row["CompanyID"].ToString());

                                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.UsersByEmail> UsersByEmailEntities = GPSTrackingDBV2.Data.DataRepository.UsersByEmailProvider.GetByUsernameCompanyId(Username, CompanyID);

                                            foreach (GPSTrackingDBV2.Entities.UsersByEmail entityMail in UsersByEmailEntities)
                                            {
                                                GPSTrackingEmailManager.Manager.Send(entityMail.Email, entityMail.Email, Message, true);
                                                GPSTrackingDBCustom.Log.MessageQueueLog.Info("User Notification Sending to: " + entityMail.Email, "Notifier");
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.GeoFenceDetails> GeoFenceDetailEntities = GPSTrackingDBV2.Data.DataRepository.GeoFenceDetailsProvider.GetByGeoFenceId(entity.GeoFenceId);
                            if (!IsPointInPolygon(GeoFenceDetailEntities, GPSMessageQueue.TrackDataLastLocationEntity))
                            {
                                GPSTrackingDBV2.Data.DataRepository.TrackDataLastLocationNotificationsProvider.Delete(TrackDataLastLocationNotificationEntity);
                            }
                        }

                    }

                }

            }
            else
            {
                GPSTrackingDBCustom.Log.MessageQueueLog.Info("Not have notifications ...", "Notifier");
            }

            //Finish Process
            GPSTrackingDBCustom.Log.MessageQueueLog.Info("Process End ... Imei:"+GPSMessageQueue.Imei, "Notifier");




            //Has Alarm imei?
            //GPSTrackingServerDB.Entities.NotificationsByImei NotificationsByImeiEntity = GPSTrackingServerDB.Data.DataRepository.NotificationsByImeiProvider.GetByNotificationIdImei(GPSMessageQueue.Code, GPSMessageQueue.Imei);

            //if (NotificationsByImeiEntity != null)
            //{
            //    //Get Message to send
            //    GPSTrackingServerDB.Entities.Notifications NotificationEntity = GPSTrackingServerDB.Data.DataRepository.NotificationsProvider.GetByNotificationId(GPSMessageQueue.Code);

            //    if (NotificationsByImeiEntity.Sms.HasValue && NotificationsByImeiEntity.LastSms.Value.Add(new TimeSpan(0,(short)NotificationsByImeiEntity.EachMinSms,0)) < DateTime.Now)
            //    {                    
            //        //Open Datacard SMS
            //        GPSTrackingSMSManager.Manager SMSManager = new GPSTrackingSMSManager.Manager();
            //        SerialPort PortDataCard = SMSManager.OpenPort(Properties.Settings.Default.PortName, Properties.Settings.Default.BaudRate, Properties.Settings.Default.DataBits, Properties.Settings.Default.ReadTimeOut, Properties.Settings.Default.WriteTimeOut);
            //        try
            //        {
            //            // Send SMS 
            //            SMSManager.sendMsg(PortDataCard, NotificationsByImeiEntity.Sms.ToString(), NotificationEntity.SmsMessage);

            //            //Update last SMS
            //            NotificationsByImeiEntity.LastSms = DateTime.Now;
            //            GPSTrackingServerDB.Data.DataRepository.NotificationsByImeiProvider.Update(NotificationsByImeiEntity);

            //            // NotificationsLog
            //            GPSTrackingLogCustom.NotificationsLog.Info(GPSMessageQueue.Imei, "Send SMS: " + NotificationsByImeiEntity.Sms.ToString() + " Message: " + NotificationEntity.SmsMessage);
            //        }
            //        catch (Exception ex)
            //        {
            //            GPSTrackingLogCustom.NotificationsLog.Error("Error Notifier ReceiveCompleted: Phone:" + NotificationsByImeiEntity.Sms.ToString() + "Message SMS:" + NotificationEntity.SmsMessage + " Message: " + ex.Message);
            //        }
                    
            //    }

            //    if (!string.IsNullOrEmpty(NotificationsByImeiEntity.Email) && NotificationsByImeiEntity.LastEmail.Value.Add(new TimeSpan(0, (short)NotificationsByImeiEntity.EachMinEmail, 0)) < DateTime.Now)
            //    {

            //        try
            //        {
            //            //Send email
            //            GPSTrackingEmailManager.Manager.Send(NotificationsByImeiEntity.Email, NotificationsByImeiEntity.Email, NotificationEntity.EmailMessage, true);

            //            //Update last email
            //            NotificationsByImeiEntity.LastEmail = DateTime.Now;
            //            GPSTrackingServerDB.Data.DataRepository.NotificationsByImeiProvider.Update(NotificationsByImeiEntity);
                        
            //            //NotificationsLog
            //            GPSTrackingLogCustom.NotificationsLog.Info(GPSMessageQueue.Imei, "Send Email: " + NotificationsByImeiEntity.Email + "Message email: " + NotificationEntity.EmailMessage);
            //        }
            //        catch (System.Net.Mail.SmtpException ex)
            //        {
            //            GPSTrackingLogCustom.NotificationsLog.Error("Error Notifier ReceiveCompleted: Email:" + NotificationsByImeiEntity.Email + "Message email:" + NotificationEntity.EmailMessage + " Message: " + ex.Message);
            //        }
            //    }
                
            //}            

            // Start an asynchronous socket to listen for connections.
            GPSTrackingDBCustom.Log.MessageQueueLog.Info("Waiting for a message .....", "Notifier");

            // Restart the asynchronous Receive operation.
            MessageQueue.BeginReceive();            
        }

        private static void InsertNotification(ref string Message, string Imei, int GeoFenceId, int NotificationId)
        {
            GPSTrackingDBV2.Entities.GpsStock GpsStockEntity = GPSTrackingDBV2.Data.DataRepository.GpsStockProvider.GetByGpsImei(Imei);

            GPSTrackingDBV2.Entities.GeoFences GeoFenceEntity = GPSTrackingDBV2.Data.DataRepository.GeoFencesProvider.GetByGeoFenceId(GeoFenceId);
            Message = string.Format(Message, GpsStockEntity.Name, GeoFenceEntity.Name, DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString());

            GPSTrackingDBV2.Entities.TrackDataLastLocationNotifications TrackDataLastLocationNotificationEntity = new GPSTrackingDBV2.Entities.TrackDataLastLocationNotifications();


            //Track DataLast Location Notification
            TrackDataLastLocationNotificationEntity.GpsImei = Imei;
            TrackDataLastLocationNotificationEntity.NotificationId = NotificationId;
            TrackDataLastLocationNotificationEntity.GeoFenceId = GeoFenceId;
            TrackDataLastLocationNotificationEntity.NotificationText = Message;

            GPSTrackingDBV2.Data.DataRepository.TrackDataLastLocationNotificationsProvider.Insert(TrackDataLastLocationNotificationEntity);


            //Notifications Track Data
            GPSTrackingDBV2.Entities.NotificationsTrackData NotificationsTrackDataEntity = new GPSTrackingDBV2.Entities.NotificationsTrackData();
            NotificationsTrackDataEntity.GpsImei = Imei;
            NotificationsTrackDataEntity.Datetime = DateTime.Now;
            NotificationsTrackDataEntity.NotificationId = NotificationId;
            NotificationsTrackDataEntity.NotificactionText = Message;

            GPSTrackingDBV2.Data.DataRepository.NotificationsTrackDataProvider.Insert(NotificationsTrackDataEntity);

        
        }


        private static bool IsPointInPolygon(GPSTrackingDBV2.Entities.TList<GPSTrackingDBV2.Entities.GeoFenceDetails> GeoFenceDetailEntities, GPSTrackingDBV2.Entities.TrackDataLastLocation TrackDataLastLocationEntity)
        {
            int i, j;
            bool PointIn = false;
            for (i = 0, j = GeoFenceDetailEntities.Count - 1; i < GeoFenceDetailEntities.Count; j = i++)
            {
                if ((((GeoFenceDetailEntities[i].Latitude <= TrackDataLastLocationEntity.Latitude) && (TrackDataLastLocationEntity.Latitude < GeoFenceDetailEntities[j].Latitude)) |
                    ((GeoFenceDetailEntities[j].Latitude <= TrackDataLastLocationEntity.Latitude) && (TrackDataLastLocationEntity.Latitude < GeoFenceDetailEntities[i].Latitude))) &&
                    (TrackDataLastLocationEntity.Longitude < (GeoFenceDetailEntities[j].Longitude - GeoFenceDetailEntities[i].Longitude) * (TrackDataLastLocationEntity.Latitude - GeoFenceDetailEntities[i].Latitude) / (GeoFenceDetailEntities[j].Latitude - GeoFenceDetailEntities[i].Latitude) + GeoFenceDetailEntities[i].Longitude))
                    PointIn = !PointIn;
            }
            return PointIn;
        }

    }
}
